// **************************************************************
// COPYRIGHT(c)2015, Xidian University
// All rights reserved.
//
// IP LIB INDEX :  
// IP Name      :      
// File name    :  
// Module name  :  
// Full name    :  
// Time         : 2016 
// Author       : Wang-Weina 
// Email        : 327422289@qq.com 
// Data         : 
// Version      : V 1.0 
// 
// Abstract     :
// Called by    :  
// 
// Modification history
// -----------------------------------------------------------------
// 
// 
//
// *****************************************************************

// *******************
// TIMESCALE
// ******************* 
`timescale 1ns/1ps 

// *******************
// INFORMATION
// *******************

//*******************
//DEFINE(s)
//*******************
//`define UDLY 1    //Unit delay, for non-blocking assignments in sequential logic
//布谷鸟哈希的原理
//一个待查找的字段哈希成两个key值，取出两个key值中的数据与原值进行比较
//任意一个相等则成查找成功，并且取得对应地址中存储的用户号
//写ram时，最快三个周期写入一个数据才不会产生错误
//*******************
//DEFINE MODULE PORT
//*******************
`include "top_define.v"
module  hash_cuckoo
    (
    input wire clk,
    input wire rst_n,
    input wire[127:0] hash_in,
    input wire lookup_en,
    //与cpu的接口
    input wire[255:0] ram_data,
    // input wire[15:0] ram_addr,
    // input wire[31:0] ram_ctrl,
    input wire cpu_wen,
    // input wire cpu_ren,

    output wire lookup_done,
    output wire lookup_success,
    output wire [4:0] addra_w,
    output reg [4:0]match_res,
    output reg wea,
    // output wire[2:0] user_id,
    output reg[4:0] error
    );

//*******************
//DEFINE LOCAL PARAMETER
//*******************
//parameter(s)
//*********************


//INNER SIGNAL DECLARATION
//*********************
//REGS
reg lookup_done_d1,lookup_success_d1;
// reg[4:0] match_res; //匹配结果
reg[127:0] hash_data,hash_data_d1,hash_data_d2;
reg hash_en;
// reg cpu_wen_d1;
// reg [3:0]cpu_wen_cnt;

reg[127:0] dina;
// reg[2:0] user_id_w,userid_d1,userid_d2,userid_d3;
reg[4:0] addra_d1,addrb_d1,addra_r;
// reg wea; 
reg wen_d1,wen_d2,wen_d3;
reg len_d1,len_d2,len_d3;

//WIRES

wire[4:0] addra,addrb;
wire[127:0] douta,doutb;
wire is_emptya,is_emptyb;
wire is_equala,is_equalb;
//*********************
//INSTANTCE MODULE
//*********************


crc_5_128_v0 U_crc_5_128_v0(
        .data_in (hash_data ) ,
        .crc_en  (hash_en   ) ,
        .crc_out (addra     ) ,
        .rst_n   (rst_n       ) ,
        .clk     (clk       )
);
hash_5_128_v1 U_crc_5_128_v1(
        .data_in (hash_data ) ,
        .crc_en  (hash_en   ) ,
        .crc_out (addrb     ) ,
        .rst_n   (rst_n     ) ,
        .clk     (clk       )
);
`ifdef ASIC
//dp_ram_d32_w128_wrapper U_ram_asic(
//  .clka(clk),
//  .clkb(clk),
//  .wea(wea),
//  .web(1'b0),
//  .addra(addra_w),
//  .addrb(addrb),
//  .dina(dina),
//  .dinb(128'h0),
//  .douta(douta),
//  .doutb(doutb)
//);
//HXF 2021.9.1
dual_ram_128_32 U_ram(
    .clka(clk),
    .wea(wea),
    .rst_n(rst_n),
    .addra(addra_w),
    .dina(dina),
    .douta(douta),
    .clkb(clk),
    .web(1'b0),
    .addrb(addrb),
    .dinb(128'h0),
    .doutb(doutb)
    );
`else
true_ram_dual U_ram(
  .clka(clk), // input clka
  .wea(wea), // input [0 : 0] wea
  .addra(addra_w), // input [8 : 0] addra
  .dina(dina), // input [31 : 0] dina
  .douta(douta), // output [31 : 0] douta
  .clkb(clk), // input clkb
  .web(1'b0), // input [0 : 0] web
  .addrb(addrb), // input [8 : 0] addrb
  .dinb(128'h0), // input [31 : 0] dinb
  .doutb(doutb) // output [31 : 0] doutb
);
`endif

//*********************
//MAIN CORE
//********************* 

//写ram的控制逻辑
assign addra_w =(wea==1'b1)?addra_r:addra;
//增加控制信号延时
// always@(posedge clk) begin
//   if(~rst_n)
//     cpu_wen_cnt    <= 0;
//   else if(cpu_wen)
//     cpu_wen_cnt    <= cpu_wen_cnt + 1 ;
//   else if(cpu_wen_cnt == 8)
//     cpu_wen_cnt    <= 0;
//   else
//     cpu_wen_cnt    <= cpu_wen_cnt;
// end
// always@(posedge clk or negedge rst_n)
//   if(~rst_n)
//     cpu_wen_d1 <= 1'b0;
//   else if(cpu_wen_cnt == 8)
//     cpu_wen_d1 <= 1'b1;
//   else 
//     cpu_wen_d1 <= 1'b0;
//hash_en 信号
//写hash_ram时需要此信号，读hash_ram时也需要此信号
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    hash_en <= 1'b0;
  else if(cpu_wen)
    hash_en <= 1'b1;
  else if(lookup_en)
    hash_en <= 1'b1;
  else 
    hash_en <= 1'b0;
//hash_data
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    hash_data <= 256'd0;
  else if(cpu_wen)
    hash_data <= ram_data[255:128];
  else if(lookup_en)
    hash_data <= hash_in;
  else 
    hash_data <= hash_data;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    hash_data_d1 <= 256'h0;
  else 
    hash_data_d1 <= hash_data;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    hash_data_d2 <= 256'h0;
  else 
    hash_data_d2 <= hash_data_d1;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    addra_d1 <= 5'h0;
  else 
    addra_d1 <= addra;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    addrb_d1 <= 5'h0;
  else 
    addrb_d1 <= addrb;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    begin 
      wen_d1 <= 1'b0;
      wen_d2 <= 1'b0;
      wen_d3 <= 1'b0;
    end 
  else 
    begin 
      wen_d1 <= cpu_wen;
      wen_d2 <= wen_d1;
      wen_d3 <= wen_d2;
    end 
// always@(posedge clk or negedge rst_n)
//   if(~rst_n)
//     begin 
//       userid_d1 <= 3'b0;
//       userid_d2 <= 3'b0;
//       userid_d3 <= 3'b0;
//       user_id_w <= 3'b0;
//     end 
//   else 
//     begin 
//       userid_d1 <= ram_data[2:0];
//       userid_d2 <= userid_d1;
//       userid_d3 <= userid_d2;
//       user_id_w <= userid_d3;
//     end 
//在哈希出来的地址中选择空地址将数据写入
assign is_emptya = (douta == 256'h0)?1'b1:1'b0;
assign is_emptyb = (doutb == 256'h0)?1'b1:1'b0;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    begin 
      len_d1 <= 1'b0;
      len_d2 <= 1'b0;
      len_d3 <= 1'b0;
    end 
  else 
    begin 
      len_d1 <= lookup_en;
      len_d2 <= len_d1;
      len_d3 <= len_d2;
    end 
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    begin
      wea     <= 1'b0  ;
      addra_r <= 5'h0  ;
      dina    <= 256'h0 ;
      error   <= 5'd0;
    end
  else if(wen_d3)
    case({is_emptya,is_emptyb})
      2'b00:
        begin
          wea     <= 1'b0  ;
          addra_r <= 5'h0  ;
          dina    <= 256'h0 ;
          error   <= error+1;
        end
      2'b10:
        begin
          wea     <= 1'b1         ;
          addra_r <= addra_d1     ;
          dina    <= hash_data_d2 ;
          error   <= error ;
        end
      2'b01:
        begin
          wea     <= 1'b1         ;
          addra_r <= addrb_d1     ;
          dina    <= hash_data_d2 ;
          error  <= error;
        end
      2'b11:
        begin
          wea     <= 1'b1         ;
          addra_r <= addra_d1     ;
          dina    <= hash_data_d2 ;
          error <= error;
        end
      default:
        begin
          wea     <= 1'b0  ;
          addra_r <= 5'h0  ;
          dina    <= 256'h0 ;
          error <= error;
        end
    endcase
  else 
    begin
      wea     <= 1'b0  ;
      addra_r <= 5'h0  ;
      dina    <= 256'h0 ;
      error   <= error;
    end
//查找时，两个取出的存储值如果和原值相等则表示查找成功
assign is_equala = (douta == hash_data_d2)?1'b1:1'b0;
assign is_equalb = (doutb == hash_data_d2)?1'b1:1'b0;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    begin 
      lookup_done_d1    <= 1'b0 ;
      match_res       <= 5'd0 ;
      lookup_success_d1 <= 1'b0 ;
    end 
  else if(len_d3)
    case({is_equala,is_equalb})
    2'b00:
      begin
        lookup_done_d1    <= 1'b1 ;
        match_res       <= 5'h0 ;
        lookup_success_d1 <= 1'b0 ;
      end
    2'b10:
      begin
        lookup_done_d1    <= 1'b1     ;
        match_res       <= addra_d1 ;
        lookup_success_d1 <= 1'b1     ;
      end
    2'b01:
      begin
        lookup_done_d1    <= 1'b1     ;
        match_res       <= addrb_d1 ;
        lookup_success_d1 <= 1'b1     ;
      end
    2'b11:
      begin
        lookup_done_d1    <= 1'b1     ;
        match_res       <= addra_d1 ;
        lookup_success_d1 <= 1'b1     ;
      end
    default:
      begin
        lookup_done_d1    <= 1'b0 ;
        match_res       <= 5'h0 ;
        lookup_success_d1 <= 1'b0 ;
      end
    endcase
  else 
    begin 
      lookup_done_d1    <= 1'b0 ;
      match_res       <= 5'd0 ;
      lookup_success_d1 <= 1'b0 ;
    end 
//2021.9.1 HXF
//always@(posedge clk or negedge rst_n)
//  if(~rst_n)
//    lookup_success <= 1'h0;
//  else 
assign    lookup_success = lookup_success_d1;

//always@(posedge clk or negedge rst_n)
//  if(~rst_n)
//    lookup_done <= 1'h0;
//  else 
assign    lookup_done = lookup_done_d1;
endmodule    // hookup byte controller block
